package com.twu.biblioteca;

import org.junit.*;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;

import static org.junit.Assert.*;

/**
 * Created by elimydlarz on 15/04/14.
 */
public class LibraryTester {

    private final String lineBreak = System.getProperty("line.separator");

    private StringBuilder testInput;
    private StringBuilder expectedOutput;

    private UserManager userManager;
    private Library library;
    private Librarian librarian;

    private final ByteArrayOutputStream consoleOutput = new ByteArrayOutputStream();

    public LibraryTester() {
        System.setOut(new PrintStream(consoleOutput));
    }

    @Before
    public void setUp() throws Exception {
        System.setIn(System.in);
        consoleOutput.reset();
        testInput = new StringBuilder();
        expectedOutput = new StringBuilder();

        HashMap<String, User> users = new HashMap<String, User>();
        users.put("123-4567", new User("123-4567", "password", "Eli", "abc.defghij@klmno.pqr", "1234 567 890"));
        userManager = new UserManager(users, null);

        HashMap<String, Book> bookShelf = new HashMap<String, Book>();
        HashMap<String, Book> checkedOutBooks = new HashMap<String, Book>();
        HashMap<String, Movie> moviesShelf = new HashMap<String, Movie>();
        HashMap<String, Movie> checkedOutMovies = new HashMap<String, Movie>();
        bookShelf.put("Neuromancer", new Book("Neuromancer"));
        bookShelf.put("Anathem", new Book("Anathem"));
        bookShelf.put("Ilium", new Book("Ilium"));
        checkedOutBooks.put("1984", new Book("1984"));
        moviesShelf.put("Oh Brother, Where Art Thou?", new Movie("Oh Brother, Where Art Thou?", 2000, "Joel Coen, Ethan Coen", 10));
        moviesShelf.put("Twelve Monkeys", new Movie("Twelve Monkeys", 1995, "Terry Gilliam", 9));
        moviesShelf.put("Lagaan: Once Upon a Time in India", new Movie("Lagaan: Once Upon a Time in India", 2001, "Ashutosh Gowariker", 7));
        checkedOutMovies.put("Fight Club", new Movie("Fight Club", 1999, "David Fincher", 8));
        library = new Library(bookShelf, checkedOutBooks, moviesShelf, checkedOutMovies);

        librarian = new Librarian(userManager, library);
    }

    @Test
    public void testWelcomeMessage() {
        expectedOutput.append("Welcome to Biblioteca.");
        expectedOutput.append(lineBreak);
        librarian.writeWelcomeMessage();
        assertEquals(expectedOutput.toString(), consoleOutput.toString());
    }

    @Test
    public void testMainMenu() {
        userManager.attemptLogIn("123-4567", "password");

        testInput.append("quit");
        System.setIn(new ByteArrayInputStream(testInput.toString().getBytes()));

        expectedOutput.append("Main menu: ");
        expectedOutput.append(lineBreak);
        expectedOutput.append("List books");
        expectedOutput.append(lineBreak);
        expectedOutput.append("List movies");
        expectedOutput.append(lineBreak);
        expectedOutput.append("Check out book");
        expectedOutput.append(lineBreak);
        expectedOutput.append("Check out movie");
        expectedOutput.append(lineBreak);
        expectedOutput.append("Return book");
        expectedOutput.append(lineBreak);
        expectedOutput.append("Return movie");
        expectedOutput.append(lineBreak);
        expectedOutput.append("User info");
        expectedOutput.append(lineBreak);
        expectedOutput.append("Log out");
        expectedOutput.append(lineBreak);
        expectedOutput.append("Quit");
        expectedOutput.append(lineBreak);
        expectedOutput.append("Bye.");
        expectedOutput.append(lineBreak);

        librarian.mainMenu();
        assertEquals(expectedOutput.toString(), consoleOutput.toString());
    }

    @Test
    public void testListBooks() {
        ArrayList<String> expectedBookTitles = new ArrayList<String>();
        expectedBookTitles.add("Anathem");
        expectedBookTitles.add("Neuromancer");
        expectedBookTitles.add("Ilium");
        assertEquals(expectedBookTitles, library.getBookTitles());
    }

    @Test
    public void testSuccessfulLogin() {
        assertTrue(userManager.attemptLogIn("123-4567", "password"));
    }

    @Test
    public void testUnsuccessfulLoginID() {
        assertFalse(userManager.attemptLogIn("000-0000", "password"));
    }

    @Test
    public void testUnsuccessfulLoginPassword() {
        assertFalse(userManager.attemptLogIn("123-4567", "not password"));
    }

    @Test
    public void testUserInfo() {
        userManager.attemptLogIn("123-4567", "password");
        String expectedName = "Eli";
        String expectedEmail = "abc.defghij@klmno.pqr";
        String expectedPhone = "1234 567 890";
        assertEquals(expectedName, userManager.getActiveUser().getName());
        assertEquals(expectedEmail, userManager.getActiveUser().getEmail());
        assertEquals(expectedPhone, userManager.getActiveUser().getPhone());
    }

    @Test
    public void testSuccessfulCheckOutBook() {
        assertTrue(library.checkOutBook("Anathem"));

        ArrayList<String> expectedBookTitles = new ArrayList<String>();
        expectedBookTitles.add("Neuromancer");
        expectedBookTitles.add("Ilium");
        assertEquals(expectedBookTitles, library.getBookTitles());
    }

    @Test
    public void testUnsuccessfulCheckOutBook() {
        assertFalse(library.checkOutBook("Do Androids Dream of Electric Sheep?"));
    }

    @Test
    public void testSuccessfulReturnBook() {
        assertTrue(library.returnBook("1984"));

        ArrayList<String> expectedBookTitles = new ArrayList<String>();
        expectedBookTitles.add("Anathem");
        expectedBookTitles.add("Neuromancer");
        expectedBookTitles.add("1984");
        expectedBookTitles.add("Ilium");
        assertEquals(expectedBookTitles, library.getBookTitles());
    }

    @Test
    public void testUnsuccessfulReturnBook() {
        assertFalse(library.returnBook("Do Androids Dream of Electric Sheep?"));
    }

    @Test
    public void testCheckOutAndReturnBook() {
        assertTrue(library.checkOutBook("Anathem"));
        assertTrue(library.returnBook("Anathem"));
    }

    @Test
    public void testListMovies() {
        ArrayList<String> expectedMovieTitles = new ArrayList<String>();
        expectedMovieTitles.add("Twelve Monkeys");
        expectedMovieTitles.add("Lagaan: Once Upon a Time in India");
        expectedMovieTitles.add("Oh Brother, Where Art Thou?");

        assertEquals(expectedMovieTitles, library.getMovieTitles());
    }

    @Test
    public void testSuccessfulCheckOutMovie() {
        assertTrue(library.checkOutMovie("Twelve Monkeys"));
        assertEquals(expectedOutput.toString(), consoleOutput.toString());

        ArrayList<String> expectedMovieTitles = new ArrayList<String>();
        expectedMovieTitles.add("Lagaan: Once Upon a Time in India");
        expectedMovieTitles.add("Oh Brother, Where Art Thou?");
        assertEquals(expectedMovieTitles, library.getMovieTitles());
    }

    @Test
    public void testUnsuccessfulCheckOutMovie() {
        assertFalse(library.checkOutMovie("Thirteen Monkeys"));
    }

    @Test
    public void testSuccessfulReturnMovie() {
        assertTrue(library.returnMovie("Fight Club"));

        ArrayList<String> expectedMovieTitles = new ArrayList<String>();
        expectedMovieTitles.add("Fight Club");
        expectedMovieTitles.add("Twelve Monkeys");
        expectedMovieTitles.add("Lagaan: Once Upon a Time in India");
        expectedMovieTitles.add("Oh Brother, Where Art Thou?");
        assertEquals(expectedMovieTitles, library.getMovieTitles());
    }

    @Test
    public void testCheckOutAndReturnMovie() {
        assertTrue(library.checkOutMovie("Twelve Monkeys"));
        assertTrue(library.returnMovie("Twelve Monkeys"));
    }
}
